#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>
#include <math.h>
#include <gmp.h> // Tag -lgmp antes de compilar
#include <pthread.h>

#define N_THREADS 2

#define KICKS  ULONG_MAX/(N_THREADS*5)
#define BASE 10
#define MANTISSA 1024*1024*32  // quanto maior pior o tempo

unsigned long acc[N_THREADS];
pthread_mutex_t MUTEX;

int get_random(){
		int fd, nrandom;
		fd = open("/dev/random", O_RDONLY);
		read(fd, &nrandom, sizeof(nrandom));
		close(fd);
		return nrandom;
}

void *kickar(void *tid){
    
    int k, i;
    double X, Y, R; 
	unsigned long hits=0;
 
    k  = (int) tid;

	for(i=0; i<KICKS; i++){

        pthread_mutex_lock(&MUTEX);
		// Distribuicao uniforme 0<x<1;
        X = rand();
        Y = rand();		    
        pthread_mutex_unlock(&MUTEX);
    
        X = X/(double)RAND_MAX;	
        Y = Y/(double)RAND_MAX;	
      
          // Pitagoras
        R = (X*X)+(Y*Y);
        R = sqrt(R); 

    	//Avaliacao
		if(R<=1) hits++;		
	}

    acc[k] = hits;
 
    pthread_exit(NULL);
}

int main(int argc, char *argv[]){
		
	time_t t_zero, t_final;
	double delta_t;
	int i, j=0, k=0;

    pthread_t threads[N_THREADS];
    pthread_attr_t attr;

    // Marca o tmepo 
	time(&t_zero);

    // Inicializa pthreads, etc..
    pthread_mutex_init(&MUTEX, NULL);
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

	// Configura seed das funcoes Random
    srand(get_random());	
	
	// Float com precisao arbitratia
	mpf_set_default_prec(MANTISSA);
	mpf_t pi, aux;
	
	for(i=0; i<N_THREADS; i++){
            pthread_create(&threads[i], &attr, kickar, (void*) i);
    }
		  
	for(i=0; i<N_THREADS; i++){
            pthread_join(threads[i], NULL);
    }

    mpf_init(pi);
    mpf_init2(aux, 128); 

    for(i=0; i<N_THREADS; i++){
            mpf_add_ui(aux, aux, acc[i]);
    }
 
    mpf_set(pi, aux);
    
    mpf_set_ui(aux, KICKS);
    mpf_mul_ui(aux, aux, N_THREADS);

    mpf_div(pi, pi, aux);
    mpf_mul_ui(pi, pi, 4);  
 	
    mpf_out_str(NULL, BASE, 0, pi);
		
	// Limpa a Caquinha
	mpf_clear(aux);
	mpf_clear(pi);

    pthread_attr_destroy(&attr);
    pthread_mutex_destroy(&MUTEX);
	
    time(&t_final);	
	delta_t  = difftime(t_final, t_zero);

	printf("\nTempo = %.2lf segundos\n", delta_t);
		
    pthread_exit(NULL);

	return 0;

}
